/*
* RapidMiner
*
* Copyright (C) 2001-2014 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapid_i.deployment.update.client;
import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.xml.ws.WebServiceException;
import com.rapidminer.deployment.client.wsimport.PackageDescriptor;
import com.rapidminer.deployment.client.wsimport.UpdateService;
import com.rapidminer.gui.RapidMinerGUI;
import com.rapidminer.gui.tools.ProgressThread;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.components.LinkButton;
import com.rapidminer.gui.tools.dialogs.ButtonDialog;
import com.rapidminer.gui.tools.dialogs.ConfirmDialog;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.NetTools;
import com.rapidminer.tools.ProgressListener;
import com.rapidminer.tools.plugin.Dependency;
/**
*
* @author Simon Fischer
*
*/
public class UpdateDialog extends ButtonDialog {
private static final long serialVersionUID = 1L;
static {
NetTools.init();
}
public static final Action UPDATE_ACTION = new ResourceAction("update_manager") {
private static final long serialVersionUID = 1L;
{
setCondition(EDIT_IN_PROGRESS, DONT_CARE);
}
@Override
public void actionPerformed(ActionEvent arg0) {
showUpdateDialog(false);
}
};
private WindowListener windowListener = new WindowListener() {
public void windowActivated(WindowEvent e) {
UpdateServerAccount account = UpdateManager.getUpdateServerAccount();
account.updatePurchasedPackages(updateModel);
}
@Override
public void windowOpened(WindowEvent e) {}
@Override
public void windowClosing(WindowEvent e) {}
@Override
public void windowClosed(WindowEvent e) {}
@Override
public void windowIconified(WindowEvent e) {}
@Override
public void windowDeiconified(WindowEvent e) {}
@Override
public void windowDeactivated(WindowEvent e) {}
};
private final UpdatePanel ulp;
private static UpdatePackagesModel updateModel;
private static class USAcountInfoButton extends LinkButton implements Observer {
private static final long serialVersionUID = 1L;
public USAcountInfoButton() {
super(new AbstractAction("") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
UpdateServerAccount account = UpdateManager.getUpdateServerAccount();
if ("#register".equals(e.getActionCommand())) {
try {
Desktop.getDesktop().browse(new URI(UpdateManager.getBaseUrl() + "/faces/signup.xhtml"));
} catch (Exception ex) {
SwingTools.showSimpleErrorMessage("cannot_open_browser", ex);
}
} else {
if (account.isLoggedIn()) {
account.logout(updateModel);
} else {
account.login(updateModel);
}
}
}
});
Dimension size = new Dimension(300, 24);
this.setSize(size);
this.setMaximumSize(size);
this.setPreferredSize(size);
}
@Override
public void update(Observable obs, Object arg) {
if (obs instanceof UpdateServerAccount) {
UpdateServerAccount account = (UpdateServerAccount) obs;
if (account.isLoggedIn()) {
this.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.update.account_button.logged_in", account.getUserName()));
} else {
this.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.update.account_button.logged_out"));
}
}
}
}
private class InstallButton extends JButton implements Observer {
private static final long serialVersionUID = 1L;
InstallButton(Action a) {
super(a);
updateButton();
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof UpdatePackagesModel) {
//UpdatePackagesModel currentModel = (UpdatePackagesModel)o;
updateButton();
}
}
private void updateButton() {
UpdatePackagesModel currentModel = updateModel;
if (currentModel.getInstallationList() != null && currentModel.getInstallationList().size() > 0) {
this.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.action.update.install.label", currentModel.getInstallationList().size()));
this.setEnabled(true);
} else {
this.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.action.update.install.label", 0));
this.setEnabled(false);
}
}
}
private USAcountInfoButton accountInfoButton = new USAcountInfoButton();
private InstallButton installButton;
private final PackageDescriptorCache packageDescriptorCache = new PackageDescriptorCache();
private boolean isConfirmed = false;
private LinkedList<PackageDescriptor> installablePackageList;
private JButton closeButton;
public UpdateDialog(String[] preselectedExtensions) {
super("update");
setModal(true);
UpdateServerAccount usAccount = UpdateManager.getUpdateServerAccount();
usAccount.addObserver(accountInfoButton);
updateModel = new UpdatePackagesModel(packageDescriptorCache, usAccount);
ulp = new UpdatePanel(this, packageDescriptorCache, preselectedExtensions, usAccount, updateModel);
closeButton = makeCloseButton();
layoutDefault(ulp, HUGE, makeOkButton(), closeButton);
this.addWindowListener(windowListener);
}
@Override
protected JButton makeOkButton(String i18nKey) {
Action okAction = new ResourceAction(i18nKey) {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
wasConfirmed = true;
ok();
}
};
installButton = new InstallButton(okAction);
getRootPane().setDefaultButton(installButton);
installButton.setEnabled(false);
updateModel.addObserver(installButton);
return installButton;
}
@Override
/** Overriding makeButtonPanel in order to display account information. **/
protected JPanel makeButtonPanel(AbstractButton... buttons) {
JPanel buttonPanel = new JPanel(new BorderLayout());
JPanel buttonPanelRight = new JPanel(new FlowLayout(FlowLayout.RIGHT, GAP, GAP));
for (AbstractButton button : buttons) {
if (button != null) {
buttonPanelRight.add(button);
}
}
buttonPanel.add(buttonPanelRight, BorderLayout.CENTER);
JPanel buttonPanelLeft = new JPanel(new FlowLayout(FlowLayout.LEFT, GAP, 2 * GAP));
buttonPanelLeft.add(accountInfoButton, false);
buttonPanel.add(buttonPanelLeft, BorderLayout.WEST);
return buttonPanel;
}
/**
* Opens the marketplace dialog and selects the update tab.
*/
public static void showUpdateDialog(final boolean selectUpdateTab, final String... preselectedExtensions) {
new ProgressThread("open_marketplace_dialog", true) {
@Override
public void run() {
getProgressListener().setTotal(100);
getProgressListener().setCompleted(33);
try {
UpdateManager.resetService();
UpdateManager.getService();
} catch (WebServiceException e) {
// thrown when no internet connection is available. Simple error message to not confuse users
SwingTools.showVerySimpleErrorMessage("failed_update_server_simple");
LogService.getRoot().log(Level.WARNING, "com.rapid_i.deployment.update.client.UpdateDialog.could_not_connect", e);
return;
} catch (Exception e) {
SwingTools.showSimpleErrorMessage("failed_update_server", e, UpdateManager.getBaseUrl());
LogService.getRoot().log(Level.WARNING, "com.rapid_i.deployment.update.client.UpdateDialog.could_not_connect", e);
return;
}
getProgressListener().setCompleted(100);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
UpdateDialog updateDialog = new UpdateDialog(preselectedExtensions);
if (selectUpdateTab) {
updateDialog.showUpdateTab();
}
updateDialog.setVisible(true);
}
});
}
}.start();
}
private void showUpdateTab() {
ulp.selectUpdatesTab();
}
public void startUpdate(final List<PackageDescriptor> downloadList) {
installButton.setEnabled(false);
new ProgressThread("resolving_dependencies", true) {
@Override
public void run() {
try {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
closeButton.setEnabled(false);
}
});
getProgressListener().setTotal(100);
final HashMap<PackageDescriptor, HashSet<PackageDescriptor>> dependency = resolveDependency(downloadList, packageDescriptorCache);
installablePackageList = getPackagesforInstallation(dependency);
getProgressListener().setCompleted(30);
final HashMap<String, String> licenseNameToLicenseTextMap = collectLicenses(installablePackageList,getProgressListener(),100,30,100);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
isConfirmed = ConfirmLicensesDialog.confirm(dependency, licenseNameToLicenseTextMap);
new ProgressThread("installing_updates", true) {
@Override
public void run() {
try {
if (isConfirmed) {
getProgressListener().setTotal(100);
getProgressListener().setCompleted(20);
UpdateService service = UpdateManager.getService();
UpdateManager um = new UpdateManager(service);
List<PackageDescriptor> installedPackages = um.performUpdates(installablePackageList, getProgressListener());
getProgressListener().setCompleted(40);
updateModel.clearFromSelectionMap(installedPackages);
ulp.validate();
ulp.repaint();
if (installedPackages.size() > 0) {
int confirmation = SwingTools.showConfirmDialog((installedPackages.size() == 1 ? "update.complete_restart" : "update.complete_restart1"),
ConfirmDialog.YES_NO_OPTION, installedPackages.size());
if (confirmation == ConfirmDialog.YES_OPTION) {
RapidMinerGUI.getMainFrame().exit(true);
} else if (confirmation == ConfirmDialog.NO_OPTION) {
if (installedPackages.size() == installablePackageList.size()) {
dispose();
}
}
}
getProgressListener().setCompleted(100);
}
} catch (Exception e) {
SwingTools.showSimpleErrorMessage("error_installing_update", e, e.getMessage());
} finally {
getProgressListener().complete();
installButton.setEnabled(true);
}
}
}.start();
}
});
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
closeButton.setEnabled(true);
}
});
getProgressListener().complete();
} catch (Exception e) {
SwingTools.showSimpleErrorMessage("error_resolving_dependencies", e, e.getMessage());
} finally {
getProgressListener().complete();
installButton.setEnabled(true);
}
}
}.start();
}
/**
* @param Start, int end
* @param Total
* @param progressListener
* @param installablePackageList2
* @return
* @throws URISyntaxException
* @throws MalformedURLException
*/
protected static HashMap<String, String> collectLicenses(LinkedList<PackageDescriptor> installablePackageList, ProgressListener progressListener, int total, int start, int end) throws MalformedURLException, URISyntaxException {
int range = end - start;
double perIterationPercent = (range * 1.0 / installablePackageList.size());
int iterCnt = 1;
HashMap<String, String> licenseNameToLicenseTextMap = null;
licenseNameToLicenseTextMap = new HashMap<String, String>();
UpdateService service = UpdateManager.getService();
for (PackageDescriptor packageDescriptor : installablePackageList) {
String licenseName = packageDescriptor.getLicenseName();
String licenseText = service.getLicenseTextHtml(licenseName);
licenseNameToLicenseTextMap.put(licenseName, licenseText);
progressListener.setCompleted((int)(start + (iterCnt*perIterationPercent)));
iterCnt++;
}
return licenseNameToLicenseTextMap;
}
@Override
protected void ok() {
ulp.startUpdate();
}
public static LinkedList<PackageDescriptor> getPackagesforInstallation(HashMap<PackageDescriptor, HashSet<PackageDescriptor>> dependency) {
HashSet<PackageDescriptor> installabledPackages = new HashSet<PackageDescriptor>();
for (PackageDescriptor packageDescriptor : dependency.keySet()) {
installabledPackages.add(packageDescriptor);
installabledPackages.addAll(dependency.get(packageDescriptor));
}
LinkedList<PackageDescriptor> installablePackageList = new LinkedList<PackageDescriptor>();
installablePackageList.addAll(installabledPackages);
return installablePackageList;
}
/**
* Recursively collect all the dependent extentions of a given extension *
* @param desc
* @param pluginsSelectedForDownload
* @return
*/
private static HashSet<Dependency> collectDependency(PackageDescriptor desc, HashSet<String> pluginsSelectedForDownload, PackageDescriptorCache packageDescriptorCache) {
HashSet<Dependency> dependencySet = new HashSet<Dependency>();
List<Dependency> dependencies = Dependency.parse(desc.getDependencies());
for (Dependency dependency : dependencies) {
String packageId = dependency.getPluginExtensionId();
PackageDescriptor packageInfo = packageDescriptorCache.getPackageInfo(packageId);
if ((!dependencySet.contains(packageInfo)) && (!pluginsSelectedForDownload.contains(packageId))) {
dependencySet.add(dependency);
dependencySet.addAll(collectDependency(packageInfo, pluginsSelectedForDownload, packageDescriptorCache));
}
}
return dependencySet;
}
public static HashMap<PackageDescriptor, HashSet<PackageDescriptor>> resolveDependency(final List<PackageDescriptor> downloadList, PackageDescriptorCache packageDescriptorCache) {
HashMap<PackageDescriptor, HashSet<PackageDescriptor>> dependentPackageMap = new HashMap<PackageDescriptor, HashSet<PackageDescriptor>>();
HashSet<String> pluginsSelectedForDownload = new HashSet<String>();
for (PackageDescriptor packageDescriptor : downloadList) {
pluginsSelectedForDownload.add(packageDescriptor.getPackageId());
dependentPackageMap.put(packageDescriptor, new HashSet<PackageDescriptor>());
}
for (PackageDescriptor desc : downloadList) {
HashSet<Dependency> dependencySet = collectDependency(desc, pluginsSelectedForDownload, packageDescriptorCache);
for (Dependency dependency : dependencySet) {
dependentPackageMap.get(desc).add(packageDescriptorCache.getPackageInfo(dependency.getPluginExtensionId()));
}
}
return dependentPackageMap;
}
}